package com.tbit.uqbike.tergateway.service.mynetty;

import com.tbit.uqbike.TerGatewayMain;
import com.tbit.uqbike.protocol.ATerPkg;
import com.tbit.uqbike.protocol.AnalyzeImpl.AAutoProtocol;
import com.tbit.uqbike.protocol.AnalyzeImpl.AProtocol;
import com.tbit.uqbike.protocol.AnalyzeImpl.TurnDeEnCoder;
import com.tbit.uqbike.protocol.AnalyzeImpl.WA206DeEnCoder;
import com.tbit.uqbike.tergateway.data.TerGatewayData;
import com.tbit.uqbike.tergateway.entity.AConnInfo;
import com.tbit.uqbike.tergateway.entity.TerTempData;
import com.tbit.uqbike.tergateway.wa206pkg.RemoteControlRsp;
import com.tbit.uqbike.tergateway.wa206pkg.WA206Head;
import com.tbit.uqbike.util.ByteUtil;
import com.tbit.uqbike.util.StringUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import org.slf4j.LoggerFactory;

import java.util.LinkedList;
import java.util.List;

public class TerProtocolPkgDecoder extends ByteToMessageDecoder {

    private static org.slf4j.Logger logger = LoggerFactory.getLogger(TerProtocolPkgDecoder.class);

    /**
     *
     */
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in,
                          List<Object> out) throws Exception {
         // 先根据连接获取协议
        String connid = ctx.channel().id().toString();
        AConnInfo connInfo = TerGatewayData.GetConnect(connid);
        if (null != connInfo) {
            AProtocol protocol = null;
            String proName = connInfo.proName;
            if (StringUtil.IsNullOrEmpty(proName)) {
                // 尝试进行协议识别
                // 收到大于等于2个字节的时候就可以进行识别
                if (in.readableBytes() > 2) {
                    protocol = TerGatewayMain.autoProtocol.autoProtocol(in);
                    if (protocol != null) {
                        connInfo.proName = protocol.getProtocolName();
                        proName = protocol.getProtocolName();
                    } else {
                        byte[] data = new byte[in.readableBytes()];
                        in.getBytes(0, data);
                        // 不能识别，直接断开连接
                        logger.info(String.format("收到未知协议的数据:%s，主动断开连接", ByteUtil.BytesToHexString(data)));
                        closeConn(ctx, in);
                        return;
                    }
                } else {
                    // 继续接收数据
                    return;
                }
            }
            //TODO 这里的协议一般是WA206DeEnCoder
            protocol = AAutoProtocol.getProtocol(proName);
            if (null != protocol) {
                // 拿到协议对象，继续数据包拆分
                List<ATerPkg> pkgList;
                try {
                    //TODO 识别到协议后，去执行协议对应的处理方法（重要）
                    pkgList = protocol.analyzeTerPkg(connInfo, in);
                } catch (Exception e) {
                    // 所有的报文解析，捕捉一下异常
                    logger.error("analyzeTerPkg", e);
                    pkgList = new LinkedList<>();
                }
                // mno检测
                // 如果收到的是转发协议，需要重新创建转发协议连接对象重新识别协议
                // 转发协议可能一个连接有多个设备需要一个一个的处理，不能通过后面的报文获取设备编号
                if (protocol instanceof TurnDeEnCoder) {
                    for (ATerPkg pkg : pkgList) {
                        connInfo = TerGatewayData.GetConnect(pkg.getConnId());
                        if (null == connInfo) {
                            continue;
                        }
                        protocol = AAutoProtocol.getProtocol(pkg.getProName());
                        if (null == protocol) {
                            continue;
                        }
                        if (StringUtil.IsNullOrEmpty(connInfo.mno)) {
                            // 如果是控制回复报文允许通过
                            if (StringUtil.IsNullOrEmpty(pkg.mno)) {
                                if (pkg instanceof RemoteControlRsp) {
                                    out.add(pkg);
                                }
                                logger.info(String.format("收到未知设备编号的报文:%s", pkg.toString()));
                                continue;
                            } else {
                                connInfo.mno = pkg.mno;
                            }
                        }
                        TerTempData ter = TerGatewayMain.terEvnet.TerRecvPkg(pkg.getConnId(), connInfo.mno, protocol.getProtocolName());
                        if (protocol instanceof WA206DeEnCoder) {
                            ter.updateTerVerReve(((WA206Head) pkg.head).version, ((WA206Head) pkg.head).reserve);
                        }
                        out.add(pkg);
                    }
                } else {
                    String mno = connInfo.mno;
                    if (StringUtil.IsNullOrEmpty(mno)) {
                        mno = getMno(pkgList);
                    }
                    // 如果设备编号为空
                    if (StringUtil.IsNullOrEmpty(mno)) {
                        if (!pkgList.isEmpty()) {
                            StringBuilder sb = new StringBuilder();
                            for (ATerPkg pkg : pkgList) {
                                if (pkg instanceof RemoteControlRsp) {
                                    out.add(pkg);
                                }
                                sb.append(pkg.toString());
                                sb.append(",");
                            }
                            logger.info(String.format("收到%d条未知设备编号的报文:%s", pkgList.size(), sb.toString()));
                        }
                    } else {
                        byte version = 0;
                        int reserve = 0;
                        for (ATerPkg pkg : pkgList) {
                            pkg.mno = mno;
                            if (protocol instanceof WA206DeEnCoder) {
                                version = ((WA206Head) pkg.head).version;
                                reserve = ((WA206Head) pkg.head).reserve;
                            }
                            out.add(pkg);
                        }
                        TerTempData ter = TerGatewayMain.terEvnet.TerRecvPkg(connid, mno, proName);
                        ter.updateTerVerReve(version, reserve);
                    }
                }
            } else {
                logger.info(String.format("协议[%s]不存在，主动断开连接", proName));
                closeConn(ctx, in);
                return;
            }
        } else {
            logger.info(String.format("连接对象[%s]不存在，主动断开连接", connid));
            closeConn(ctx, in);
            return;
        }
    }

    private void closeConn(ChannelHandlerContext ctx, ByteBuf in) {
        //in.release();
        in.clear();
        ctx.close();
    }

    /**
     * 获取设备编号
     */
    private String getMno(List<ATerPkg> pkgList) {
        String mno = "";
        for (ATerPkg pkg : pkgList) {
            if (!StringUtil.IsNullOrEmpty(pkg.mno)) {
                mno = pkg.mno;
                break;
            }
        }
        return mno;
    }
}
